package cg.cmm.base.log.aspect;

import cg.cmm.base.log.annotation.Log;
import cg.cmm.base.log.dto.LogDTO;
import cg.cmm.base.utils.RequestIdUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Date;

/**
 * @author chong
 * @date 2024/1/12
 * 日志切面 打印http请求中的请求参数 包路径 方法名
 **/
@Aspect
@Component
@Slf4j
public class LogAspect {

    @Autowired
    private ApplicationContext applicationContext;

    @Pointcut("@annotation(cg.cmm.base.log.annotation.Log) || " +
            "@within(cg.cmm.base.log.annotation.Log)")
    public void logPointCut() {
    }

    @Around("logPointCut()")
    public Object aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
        Log logAnnotation = getAnnotationLog(joinPoint);
        if (logAnnotation == null) {
            return joinPoint.proceed();
        }
        LogDTO logDTO = createLogDTO(joinPoint, logAnnotation);
        logDTO.setTraceId(RequestIdUtils.getRequestId());
        long startTime = System.currentTimeMillis();
        Object result = null;
        try {
            logDTO.setStartOperationTime(new Date());
            result = joinPoint.proceed();
        } catch (Exception t) {
            logDTO.setExceptionMessage(getStackTrace(t));
            throw t;
        } finally {
            long endTime = System.currentTimeMillis();
            long costTime = endTime - startTime;
            logDTO.setTraceId(RequestIdUtils.getRequestId());
            logDTO.setEndOperationTime(new Date());
            logDTO.setParaVal(joinPoint.getArgs());
            logDTO.setResultObj(result);
            logDTO.setExecutionTime(costTime);
            sendLog(logDTO);
        }
        return result;
    }

//    @AfterThrowing(value = "logPointCut()", throwing = "e")
//    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
//        handleBusinessAlert(joinPoint, e);
//    }
//
//    private void handleBusinessAlert(final JoinPoint joinPoint, final Exception ex) {
//        LOG.error("异常信息: {}", getStackTrace(ex));
//        RequestIdUtil.clear();
//    }

    private Log getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        return method.getAnnotation(Log.class);
    }

    private LogDTO createLogDTO(ProceedingJoinPoint joinPoint, Log logAnnotation) {
        LogDTO logDTO = new LogDTO();
        logDTO.setBusinessType(logAnnotation.businessType());
        logDTO.setOperatorType(logAnnotation.operatorType());
        logDTO.setSaveRequestData(logAnnotation.isSaveRequestData());
        logDTO.setTitle(logAnnotation.title());
        logDTO.setClassName(joinPoint.getTarget().getClass().getName());
        logDTO.setMethodName(joinPoint.getSignature().getName());
        return logDTO;
    }

    private void sendLog(LogDTO logDTO) {
        applicationContext.publishEvent(new LogProductEvent(this, logDTO));
    }

    private String getStackTrace(Throwable e) {
        StringWriter stringWriter = new StringWriter();
        PrintWriter writer = new PrintWriter(stringWriter);
        e.printStackTrace(writer);
        return stringWriter.toString();
    }
}
